package myCobra

import (
	"fmt"
	"os"
	"strings"

	flag "github.com/spf13/pflag"
)

//Command is just that, a command for your application.
type Command struct {
	// Use is the one-line usage message.
	Use string

	// Short is the short description shown in the 'help' output.
	Short string

	// Long is the long message shown in the 'help <this-command>' output.
	Long string

	// commands is the list of commands supported by this program.
	commands []*Command

	// parent is a parent command for this command.
	parent *Command

	//the sub command which are used
	sub *Command

	// Run: Typically the actual work function. Most commands will only implement this.
	Run func(cmd *Command, args []string)

	// args is actual args parsed from flags.
	args []string

	pflags *flag.FlagSet
}

/**
 * AddCommand is used to add subcommands.<br/>
 * 添加子命令
 */
func (c *Command) AddCommand(subCommand *Command) {
	for _, v := range c.commands {
		if v == subCommand {
			return
		}
	}
	c.commands = append(c.commands, subCommand)
	subCommand.parent = c
}

/**
 * Execute calls the getArgs and execute.<br/>
 * 调用getArgs和execute方法
 */
func (c *Command) Execute() error {
	if c == nil {
		return fmt.Errorf("The command is nil")
	}
	if c.parent == nil { //the root command
		getArgs(c, os.Args[1:])
	}
	c.execute()
	return nil
}

/**
 * execute is the true method to run the command.<br/>
 * 真正的执行函数
 */
func (c *Command) execute() {
	if c.sub == nil {
		for _, v := range c.args {
			if v == "-h" || v == "--help" {
				c.PrintHelp()
				return
			}
		}
		c.Run(c, c.args)
		return
	}
	c.sub.execute()
}

/**
 * getArgs is used to parse the args.<br/>
 * 用于解析参数
 */
func getArgs(c *Command, args []string) {
	if len(args) < 1 {
		return
	}
	for _, v := range c.commands { //traverse all subcommands
		if v.Use == args[0] {
			c.args = args[:1]
			c.sub = v
			getArgs(v, args[1:])
			return
		}
	}
	c.args = args
	c.PersistentFlags().Parse(c.args)
}

func (c *Command) PersistentFlags() *flag.FlagSet {
	if c.pflags == nil {
		c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
	}
	return c.pflags
}

// Name is used to return the command's name
func (c *Command) Name() string {
	name := c.Use
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) PrintHelp() {
	fmt.Printf("%s\n\n", c.Long)
	fmt.Printf("Usage:\n")
	fmt.Printf("\t%s [flags]\n", c.Name())
	if len(c.commands) > 0 {
		fmt.Printf("\t%s [command]\n\n", c.Name())
		fmt.Printf("Available Commands:\n")
		for _, v := range c.commands {
			fmt.Printf("\t%-10s%s\n", v.Name(), v.Short)
		}
	}
	fmt.Printf("\nFlags:\n")
	c.PersistentFlags().VisitAll(func(flag *flag.Flag) {
		fmt.Printf("\t-%1s, --%-6s %-12s%s (default \"%s\")\n", flag.Shorthand, flag.Name, flag.Value.Type(), flag.Usage, flag.DefValue)
	})

	fmt.Printf("\t-%1s, --%-19s%s%s\n", "h", "help", "help for ", c.Name())
	fmt.Println()

	if len(c.commands) > 0 {
		fmt.Printf("Use \"%s [command] --help\" for more information about a command.\n", c.Name())
	}
	fmt.Println()
}